<?php

class Workset_Module {

	private static $_modules = array();

	private static $_moduleDependences = array();

	/**
	 * Регистрирует новый модуль.
	 * Возвращает true если модуль добавлен или false если модуль был добавлен ранее.
	 *
	 * @static
	 * @param  $module
	 * @return boolean
	 */
	static function addModule($module) {
		if (!self::hasModule($module)) {
			self::$_modules[$module] = array();
			return true;
		}

		return false;
	}

	/**
	 * Проверяет наличие добавленного модуля
	 *
	 * @static
	 * @param  $module
	 * @return boolean
	 */
	static function hasModule($module) {
		return isset(self::$_modules[$module]);
	}

    /**
     * Возвращает список зарегистрированных модулей
     *
     * @static
     * @return array
     */
    static function getRegisteredModules() {
        return array_keys(self::$_modules);
    }

	/**
	 * Добавляет настройки для указанного модуля.
	 *
	 * @static
	 * @throws Zend_Exception
	 * @param  $module
	 * @param  $options
	 * @return void
	 */
	static function addModuleOptions($module, $options) {
		if (!self::hasModule($module)) {
			throw new Zend_Exception('Module '.$module.' does not exists');
		}

		if ($options instanceof Zend_Config) {
			$options = $options->toArray();

		} elseif (!is_array($options)) {
            throw new Zend_Exception('Invalid config format');
        }

		if (empty(self::$_modules[$module])) {
			self::$_modules[$module] = $options;
		} else {
			self::$_modules[$module] = array_merge(self::$_modules[$module], $options);
		}

	}

	/**
	 * Возвращает настройки для указанного модуля
	 *
	 * @static
	 * @throws Zend_Exception
	 * @param  $module
	 * @return
	 */
	static function getModuleOptions($module, $keys = null) {
		if (!self::hasModule($module)) {
			throw new Zend_Exception('Module '.$module.' does not exists');
		}

        if (null !== $keys) {
            if (!isset(self::$_modules[$module])) {
                return null;
            }

            if (!is_array($keys)) {
                return self::$_modules[$module][$keys];
            } else {
                return array_intersect_key(self::$_modules[$module], $keys);
            }
        }

		return isset(self::$_modules[$module]) ? self::$_modules[$module] : array();
	}

	/**
	 * Добавляет зависимость между модулями
	 *
	 * @static
	 * @throws Zend_Exception
	 * @param  $module
	 * @param  $dependentModule
	 * @return void
	 */
	static function addModuleDependence($module, $dependentModule) {
		if (!self::hasModule($module)) {
			throw new Zend_Exception('Module '.$module.' does not exists');
		}

		if (!self::hasModule($dependentModule)) {
			throw new Zend_Exception('Module '.$dependentModule.' does not exists');
		}

		if (!isset(self::$_moduleDependences[$dependentModule])) {
			self::$_moduleDependences[$dependentModule] = array();
		}

		self::$_moduleDependences[$dependentModule][] = $module;

	}

	/**
	 * Добавляет зависимости для указанного модуля
	 *
	 * @static
	 * @param  $module
	 * @param  $dependences
	 * @return void
	 */
	static function addModuleDependences($module, array $dependences) {
		foreach ($dependences as $d) {
			self::addModuleDependence($module, $d);
		}
	}

	/**
	 * Проверяет, есть ли у модуля зависимости
	 *
	 * @static
	 * @param  $module
	 * @return bool
	 */
	static function hasDependences($module) {
		if (!self::hasModule($module)) {
			return false;
		} else {
			return !empty(self::$_moduleDependences[$module]);
		}
	}

	/**
	 * Возвращает зависимости модуля
	 *
	 * @static
	 * @param  $module
	 * @return array
	 */
	static function getDependences($module) {
		if (!self::hasModule($module) || !isset(self::$_moduleDependences[$module])) {
			return array();
		} else {
			return self::$_moduleDependences[$module];
		}
	}

    /**
	 * Загружает указанный модуль
	 *
	 * @static
	 * @param  $module
	 * @return bool
	 */
    static function loadModule($module, $options = null) {
        if (!self::hasModule($module)) {
            throw new Zend_Exception('Module '.$module.' does not exists');
        }

        if (null !== $options) {
            self::addModuleOptions($module, $options);
        }

        $options = self::getModuleOptions($module);

        if (!empty($options['loaded'])) {
            return true;
        }

        //загрузчик ресурсов
        if (!empty($options['resource_loader'])) {
            $resTypes = isset($options['resourceTypes']) ? $options['resourceTypes'] : null;
			self::_initResourceLoader($module, $resTypes);
        }

        //загрузка bootstrap модуля
        if (!empty($options['bootstrap'])) {
            self::_loadModuleBootstrap($module, $options['bootstrap']);
        }

        //зависимости модуля
        if (!empty($options['dependences'])) {
            self::addModuleDependences($module, (array) $options['dependences']);
        }

        self::addModuleOptions($module, array('loaded' => true));

		return true;
    }

    /**
     * @static
     * @param  $module
     * @return bool
     */
    static function isModuleLoaded($module) {
        $options = self::getModuleOptions($module);
        return isset($options['loaded']) && $options['loaded'] == true;
    }

    private static function _loadModuleBootstrap($module, $resources) {

        $dir = Zend_Controller_Front::getInstance()->getControllerDirectory($module);

        if (!is_array($resources) && !is_string($resources)) {
            $resources = null;
        }

		$bootstrap = Zend_Controller_Front::getInstance()->getParam('bootstrap');
		if (null === $bootstrap && Zend_Registry::isRegistered('bootstrap')) {
			$bootstrap = Zend_Registry::get('bootstrap');
		}

		if (!$bootstrap instanceof Zend_Application_Bootstrap_BootstrapAbstract) {
			throw new Zend_Exception('Application bootstrap not found');
		}

        $bootstrapClass = self::_formatModuleName($module) . '_Bootstrap';

        if (!class_exists($bootstrapClass, false)) {
			$bootstrapPath  = dirname($dir) . '/Bootstrap.php';
			if (file_exists($bootstrapPath)) {
				include_once $bootstrapPath;

				if (!class_exists($bootstrapClass, false)) {
					throw new Zend_Exception("$bootstrapClass not found in path $bootstrapPath");
				}

				$moduleBootstrap = new $bootstrapClass($bootstrap);
            	$moduleBootstrap->bootstrap($resources);
			}
        }
    }

	private static function _initResourceLoader($module, $resTypes = null) {
		if (!is_array($resTypes)) {
			$resTypes = array(
	            'model'   => array(
	                'namespace' => 'Model',
	                'path'      => 'models',
	            ),
                'dbtable' => array(
                    'namespace' => 'Table',
                    'path'      => 'tables',
                ),
                'dbrow' => array(
                    'namespace' => 'Row',
                    'path'      => 'tables/rows',
                ),
    			'form' => array(
                	'namespace' => 'Form',
                	'path'      => 'forms',
	            ),
	            'plugin' => array(
	            	'namespace' => 'Plugin',
                	'path'      => 'plugins',
	            ),
	            'helper' => array(
	            	'namespace' => 'Helper',
                	'path'      => 'helpers',
	            )
    		);
		}

		$dir = Zend_Controller_Front::getInstance()->getControllerDirectory($module);

        if (null === $dir) {
            throw new Zend_Exception('Controller directory of module "'.$module.'" does not exists');
        }

        $moduleName = ucfirst($module);
        $moduleDirectory = dirname($dir);

        $autoloader = new Zend_Loader_Autoloader_Resource(array(
            'namespace' => $moduleName,
            'basePath' => $moduleDirectory
        ));

        $autoloader->addResourceTypes($resTypes);
	}

    private static function _formatModuleName($name)
    {
        $name = strtolower($name);
        $name = str_replace(array('-', '.'), ' ', $name);
        $name = ucwords($name);
        $name = str_replace(' ', '', $name);
        return $name;
    }

}

?>